using System;
using System.Collections.Generic;
using System.Text;
using MacomberMap.Common.Database_Interface.CIM.Components;
using System.Xml;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using MacomberMap.Common.Database_Interface.CIM;
using System.IO;
using MacomberMap.Common.Components;
using MacomberMap.Common.Internals;
using MacomberMap.Common.Types;
using System.Reflection;
using MacomberMap.Common.User_Interfaces.One_Lines.Elements;

namespace MacomberMap.Loader.Export.OneLines
{
    /// <summary>
    /// This class is responsible for exporting a Macomber Map one-line out to DDL format
    /// </summary>
    public static class MM_DDL_Exporter
    {
        #region Variable declarations
        /// <summary>Our offset point for coordinates</summary>
        private static Point Offset = new Point(180, 0);
        #endregion

        #region One-line exporting
        /// <summary>
        /// Export a one-line out to the specified path
        /// </summary>
        /// <param name="OneLine">The one-lines to be exported</param>
        /// <param name="ModelID">The model to be used</param>
        /// <param name="TargetFile">The target path name</param>
        /// <param name="SCADA">Whether the one-line is for SCADA or NETMOM</param>
        /// <param name="OneLineImage">The image of the one-line</param>
        public static void ExportOneLine(MM_Database_OneLine OneLine, int ModelID,  string TargetFile, bool SCADA, Image OneLineImage)
        {
            //Determine our base element
            CIM_Element BaseElement;
            if (OneLine.Document.DocumentElement.HasAttribute("rdfID"))
                BaseElement = MM_Database_CIM.Elements[new MM_rdfID(OneLine.Document.DocumentElement.Attributes["rdfID"].Value)];
            else
                BaseElement = MM_Database_CIM.TEIDs[Convert.ToUInt64(OneLine.Document.DocumentElement.Attributes["BaseElement.TEID"].Value)];


            //Then, write out our file
            String OutPath = Path.Combine(Path.GetDirectoryName(TargetFile), Path.GetFileNameWithoutExtension(TargetFile)).Replace("_dset", "").Replace("_eset", "") + "_dset.ddl";
            using (MM_DDL_Exporter_Stream OutStream = new MM_DDL_Exporter_Stream(OutPath))
            {
                OutStream.WriteHeader("WebFG 3.5.0, Builder 5.6.0");
                WriteDisplay(OutStream, OneLine, SCADA, OneLineImage, BaseElement);
                OutStream.Close();
                //Process.Start(OutPath);
            }
        }


        /// <summary>
        /// Write a one-line display out
        /// </summary>
        /// <param name="OutStream">The outgoing stream</param>
        /// <param name="OneLine">The one-line to be exported</param>
        /// <param name="SCADA">Whether this target display is for SCADA</param>
        /// <param name="OneLineImage">The one-line image</param>
        /// <param name="BaseElement">The one-line element</param>
        public static void WriteDisplay(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, Image OneLineImage, CIM_Element BaseElement)
        {
            //First, write out our header and map
            WriteHeader(OutStream, OneLine, SCADA, OneLineImage, BaseElement);

            WriteMap(OutStream, OneLine, BaseElement);


            //Then, write out every node
            foreach (XmlElement xElem in OneLine.Document["One_Line"]["Nodes"].ChildNodes)
                WriteNode(OutStream, OneLine, SCADA, BaseElement, xElem);


            //Then, write out every element
            foreach (XmlElement xElem in OneLine.Document["One_Line"]["Elements"].ChildNodes)
                WriteElement(OutStream, OneLine, SCADA, BaseElement, xElem);

            OutStream.WriteEndTag();
            OutStream.WriteEndTag();

        }
        #endregion

        #region One-line component exporting
        /// <summary>
        /// Write out our header information
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="OneLineImage"></param>
        /// <param name="BaseElement">Our base element of this display</param>
        private static void WriteHeader(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, Image OneLineImage, CIM_Element BaseElement)
        {
            //Write the header information for our display
            OutStream.WriteStartTag("display", BaseElement.Name.ToUpper());

            //Write our title
            OutStream.WriteLine();
            OutStream.WriteAttribute("title", "localize", "%DIS%,%DISAPP%[%DISFAM%]    %HOST% (%VP%)    %REF%");


            //Write out our applications and high level data
            if (SCADA)
                OutStream.WriteAttribute("application", "SCADA");
            else
                foreach (String str in "RTNET,SMTNET,STNET,DTSPSM".Split(','))
                    OutStream.WriteAttribute("application", str);
            OutStream.WriteAttribute("color", "Black");
            OutStream.WriteAttribute("scale_to_fit_style(XY)");
            OutStream.WriteStartTag("menu_bar_item", "ERCOT_DISPLAYS");
            OutStream.WriteAttribute("label", "localize", "Onelines");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteEndTag();
            OutStream.WriteAttribute("horizontal_unit", 10);
            OutStream.WriteAttribute("vertical_unit", 10);
            OutStream.WriteAttribute("horizontal_page", 50);
            OutStream.WriteAttribute("vertical_page", 50);
            OutStream.WriteAttribute("refresh", 2);
            OutStream.WriteAttribute("not locked_in_viewport");
            OutStream.WriteAttribute("horizontal_scroll_bar");
            OutStream.WriteAttribute("vertical_scroll_bar");
            OutStream.WriteAttribute("not std_menu_bar");
            OutStream.WriteAttribute("not command_window");
            OutStream.WriteAttribute("not on_top");
            OutStream.WriteAttribute("not ret_last_tab_pnum");
            OutStream.WriteAttribute("default_zoom(1.0000000)");
            OutStream.WriteStartTag("simple_layer", "NETWORK");

            //Now, write our header bar
            WriteHeaderBar(OutStream, OneLine, SCADA, OneLineImage, BaseElement);
        }

        /// <summary>
        /// Write out the header bar
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="OneLineImage"></param>
        /// <param name="BaseElement">Our vase element</param>
        private static void WriteHeaderBar(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, Image OneLineImage, CIM_Element BaseElement)
        {

            //Write our hidden substation information
            if (SCADA)
            {
                OutStream.WriteStartTag("picture", "INVISIBLE_STATION_SCADA");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("origin", 364, 10);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteKey("SUBSTN=" + BaseElement.Name);
                OutStream.WriteEndTag();
            }
            else
            {
                OutStream.WriteStartTag("picture", "INVISIBLE_STATION_NETMOM");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("cam", "NETAPP_TITLE_BACKGROUND");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("origin", 364, 10);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteKey("ST=" + BaseElement.Name);
                OutStream.WriteEndTag();
            }
            //Write the bar on top
            OutStream.WriteStartTag("rectangle", "");
            String Background = SCADA ? "MediumAquamarine" : "SteelBlue";
            if (SCADA)
            {
                OutStream.WriteStartTag("gab", "");
                OutStream.WriteAttribute("foreground", Background);
                OutStream.WriteAttribute("fill");
                OutStream.WriteEndTag();
            }
            else
            {
                OutStream.WriteAttribute("cam", "NETAPP_TITLE_BACKGROUND");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("gab", "PRIM_ROW_BACKGROUND");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
            }
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteAttribute("origin", 1, 1);
            OutStream.WriteAttribute("width", OneLineImage.Width + Offset.X);
            OutStream.WriteAttribute("height", 49);
            OutStream.WriteEndTag();

            //Write our titles
            OutStream.WriteText("Exported on: " + DateTime.Now.ToString(), new Point(4, 5), "White", Background, "Arial", 12, false, false, false, true, true);
            OutStream.WriteText("CIM file: " + Path.GetFileNameWithoutExtension(MM_Database_CIM.CIMFileName), new Point(4, 23), "White", Background, "Arial", 12, false, false, false, true, true);
            Single Latitude = XmlConvert.ToSingle(OneLine.Document.DocumentElement.Attributes["BaseElement.Latitude"].Value);
            Single Longitude = XmlConvert.ToSingle(OneLine.Document.DocumentElement.Attributes["BaseElement.Longitude"].Value);
            String County = OneLine.Document.DocumentElement.HasAttribute("BaseElement.County") ? OneLine.Document.DocumentElement.Attributes["BaseElement.County"].Value : "Unknown";
            OutStream.WriteText("Coordinates: " + Latitude.ToString("#.000") + ", " + Longitude.ToString("#.000"), new Point(OneLineImage.Width, 5), "White", Background, "Arial", 12, false, false, false, true, true);                        
            OutStream.WriteText("County: " + County, new Point(OneLineImage.Width, 23), "White", Background, "Arial", 12, false, false, false, true, true);




            //Write out substation info
            String LongName = OneLine.Document.DocumentElement.Attributes["BaseElement.LongName"].Value;
            String Name = OneLine.Document.DocumentElement.Attributes["BaseElement.Name"].Value;
            if (String.IsNullOrEmpty(LongName) || LongName.Equals(Name, StringComparison.CurrentCultureIgnoreCase))
                OutStream.WriteText(Name, new Point(380, 8), "White", "Black", "Arial", 20, true, false, false, true, true);
            else
                OutStream.WriteText(LongName + " (" + Name + ")", new Point(380, 8), "White", "Black", "Arial", 20, true, false, false, true, true);


        }


        /// <summary>
        /// Write out our map on the display
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="BaseElement">The base element</param>
        public static void WriteMap(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, CIM_Element BaseElement)
        {

            //First, place our elements
            String[] splStr = "MAP_ONELINE_BACKGROUND,0,51,MAP_STATE,0,231".Split(',');
            for (int a = 0; a < splStr.Length; a += 3)
            {
                OutStream.WriteStartTag("symbol", splStr[a]);
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("origin", int.Parse(splStr[a + 1]), int.Parse(splStr[a + 2]));
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteEndTag();
            }

            //Now, draw our state crosshairs
            PointF LatLng = new PointF(Convert.ToSingle(OneLine.Document.DocumentElement.Attributes["BaseElement.Longitude"].Value), Convert.ToSingle(OneLine.Document.DocumentElement.Attributes["BaseElement.Latitude"].Value));
            MM_Boundary StateBoundary = MM_Repository.StateBoundary;


            Point SubstationPoint = Point.Round(new PointF(180f * (LatLng.X - StateBoundary.Min.X) / (StateBoundary.Max.X - StateBoundary.Min.X), 180f * (LatLng.Y - StateBoundary.Max.Y) / (StateBoundary.Min.Y - StateBoundary.Max.Y)));
            Point MinPoint, MaxPoint;
            MinPoint = MaxPoint = SubstationPoint;
            Dictionary<XmlElement, Point> LinePoints = new Dictionary<XmlElement, Point>();
            Dictionary<UInt64, PointF> LineLatLngs = new Dictionary<ulong, PointF>();


            PointF CenterPoint = new PointF(90, 90);
            float MileDistance = float.NaN;
            List<XmlElement> Lines = new List<XmlElement>();
            foreach (XmlElement xElem in OneLine.Document.SelectNodes("One_Line/Elements/Line"))
                Lines.Add(xElem);


            //Pull in our lines and determine our mile distance            
            foreach (XmlElement xLine in Lines)
            {
                Single LineDistance = XmlConvert.ToSingle(xLine.Attributes["BaseElement.Length"].Value);
                if (float.IsNaN(MileDistance) || MileDistance < LineDistance)
                    MileDistance = LineDistance;
            }

            if (!float.IsNaN(MileDistance))
                MileDistance *= 1.2f;

            //Now go through and render all lines in the detailed view
            foreach (XmlElement xLine in Lines)
            {
                float DistanceRatio = CenterPoint.X * Convert.ToSingle(xLine.Attributes["BaseElement.Length"].Value) / MileDistance;
                float Angle;
                string[] splStr2 = xLine.Attributes["BaseElement.Coordinates"].Value.Split(',');
                PointF LineLatLng = new PointF(Convert.ToSingle(splStr2[0]), Convert.ToSingle(splStr2[1]));
                if (LatLng == LineLatLng)
                    LineLatLng = new PointF(Convert.ToSingle(splStr2[2]), Convert.ToSingle(splStr2[3]));
                LineLatLngs.Add(Convert.ToUInt64(xLine.Attributes["BaseElement.TEID"].Value), LineLatLng);

                if (xLine.HasAttribute("BaseElement.Angle"))
                    Angle = Convert.ToSingle(xLine.Attributes["BaseElement.Angle"].Value);
                else
                {
                    Angle = (float)Math.Atan2((LatLng.Y - LineLatLng.Y), (LatLng.X - LineLatLng.X));
                    xLine.Attributes.Append(xLine.OwnerDocument.CreateAttribute("BaseElement.Angle")).Value = Angle.ToString();
                }


                Point TargetPoint = Point.Round(new PointF((float)Math.Cos(Angle) * DistanceRatio + CenterPoint.X, -(float)Math.Sin(Angle) * DistanceRatio + CenterPoint.Y));
                OutStream.WriteStartTag("polyline", "");
                String KVLevel = xLine.Attributes["BaseElement.KVLevel"].Value;
                OutStream.WriteAttribute("gab \"" + KVLevel.Replace(' ', '_').ToUpper() + "\"");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("origin", TargetPoint.X, TargetPoint.Y + 48);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteAttribute("point", TargetPoint.X, TargetPoint.Y + 48);
                OutStream.WriteAttribute("point", CenterPoint.X - TargetPoint.X, CenterPoint.Y - TargetPoint.Y);
                OutStream.WriteEndTag();

                //Now, also write out our label
                OutStream.WriteStartTag("text", "");
                OutStream.WriteAttribute("gab \"HYPERLINK\"");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                CIM_Element OtherStation = null;
                foreach (String LineStation in xLine.Attributes["BaseElement.ConnectedStations"].Value.Split(','))
                    if (LineStation != BaseElement.TEID)
                        OtherStation = MM_Database_CIM.TEIDs[Convert.ToUInt64(LineStation)];

                if (String.IsNullOrEmpty(OtherStation.LongName) || OtherStation.LongName == OtherStation.Name)
                    OutStream.WriteAttribute("tool_tip(localize \"Navigate to " + OtherStation.Name + "\" )");
                else
                    OutStream.WriteAttribute("tool_tip(localize \"Navigate to " + OtherStation.LongName + " (" + OtherStation.Name + ")\" )");


                //OutStream.WriteAttribute("keyset \"" + OtherStation.Name.ToUpper() + "_CALLUP\"");
                //OutStream.WriteAttribute("set", "ERCOTONELINES");



                //double Angle = Math.Atan2(TargetPoint.Y - CenterPoint.Y, TargetPoint.X - CenterPoint.X);
                //double Dist = Math.Sqrt(Math.Pow(TargetPoint.Y - CenterPoint.Y, 2) + Math.Pow(TargetPoint.X - CenterPoint.X, 2)) + 5;
                //OutStream.WriteAttribute("origin", (float)Math.Round(Math.Cos(Angle) * Dist)+CenterPoint.X, (float)Math.Round(Math.Sin(Angle)*Dist)+CenterPoint.Y);                        
                OutStream.WriteAttribute("origin", TargetPoint.X, TargetPoint.Y + 48);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteAttribute("localize \"" + OtherStation.LongName + "\"");
                OutStream.WriteEndTag();

                //Add our main map point
                PointF OtherLatLng = LineLatLngs[XmlConvert.ToUInt64(xLine.Attributes["BaseElement.TEID"].Value)];
                Point OtherPoint = Point.Round(new PointF(180f * (OtherLatLng.X - StateBoundary.Min.X) / (StateBoundary.Max.X - StateBoundary.Min.X), 180f * (OtherLatLng.Y - StateBoundary.Max.Y) / (StateBoundary.Min.Y - StateBoundary.Max.Y)));
                LinePoints.Add(xLine, OtherPoint);

                //Add in our extra information to help us out
                xLine.Attributes.Append(xLine.OwnerDocument.CreateAttribute("BaseElement.OtherSub.Longitude")).Value = OtherPoint.X.ToString();
                xLine.Attributes.Append(xLine.OwnerDocument.CreateAttribute("BaseElement.OtherSub.Latitude")).Value = OtherPoint.Y.ToString();


                MinPoint.X = Math.Min(MinPoint.X, OtherPoint.X);
                MinPoint.Y = Math.Min(MinPoint.Y, OtherPoint.Y);
                MaxPoint.X = Math.Max(MaxPoint.X, OtherPoint.X);
                MaxPoint.Y = Math.Max(MaxPoint.Y, OtherPoint.Y);


            }




            //Now, write our legend                
            OutStream.WriteStartTag("polyline", "");
            OutStream.WriteAttribute("gab \"TEXTCOLOR\"");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("origin", 2, 38);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteAttribute("point", 3, 64);
            OutStream.WriteAttribute("point", 0, -5);
            OutStream.WriteAttribute("point", 46, 0);
            OutStream.WriteAttribute("point", 0, 5);
            OutStream.WriteEndTag();

            OutStream.WriteStartTag("text", "");
            OutStream.WriteAttribute("gab \"SMALLTEXTCOLOR\"");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("origin", 7, 70);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteAttribute("localize \"" + MileDistance.ToString("0.0") + " mi\"");
            OutStream.WriteEndTag();


            //Now write our main map.
            MinPoint.X -= 2;
            MinPoint.Y -= 2;
            MaxPoint.X += 2;
            MaxPoint.Y += 2;

            //Draw our rectangle around our point
            OutStream.WriteStartTag("rectangle", "");
            OutStream.WriteAttribute("gab \"TEXTCOLOR\"");
            OutStream.WriteAttribute("set", "ERCOTONELINES");

            OutStream.WriteAttribute("origin", MinPoint.X, MinPoint.Y + 231);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteAttribute("width", MaxPoint.X - MinPoint.X);
            OutStream.WriteAttribute("height", MaxPoint.Y - MinPoint.Y);
            OutStream.WriteEndTag();


            //Now, draw our cross-hairs            
            Point CenterMap = MM_OneLine_Element.CenterRect(Rectangle.FromLTRB(MinPoint.X, MinPoint.Y, MaxPoint.X, MaxPoint.Y));
            for (int a = 0; a < 4; a++)
            {
                OutStream.WriteStartTag("polyline", "");
                OutStream.WriteAttribute("gab \"TEXTCOLOR\"");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                Point From, To;
                if (a == 0)
                {

                    From = new Point(0, 231 + SubstationPoint.Y);
                    To = new Point(MinPoint.X, 231 + SubstationPoint.Y);
                }
                else if (a == 1)
                {
                    From = new Point(MaxPoint.X, 231 + SubstationPoint.Y);
                    To = new Point(180, 231 + SubstationPoint.Y);
                }
                else if (a == 2)
                {
                    From = new Point(SubstationPoint.X, 231);
                    To = new Point(SubstationPoint.X, 231 + MinPoint.Y);
                }
                else
                {
                    From = new Point(SubstationPoint.X, 231 + MaxPoint.Y);
                    To = new Point(SubstationPoint.X, 231 + 180);
                }
                OutStream.WriteAttribute("origin", From.X, From.Y);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteAttribute("point", From.X, From.Y);
                OutStream.WriteAttribute("point", To.X - From.X, To.Y - From.Y);
                OutStream.WriteEndTag();
            }

            //Now, draw our lines
            foreach (KeyValuePair<XmlElement, Point> kvp in LinePoints)
            {

                OutStream.WriteStartTag("polyline", "");
                OutStream.WriteAttribute("gab \"" + kvp.Key.Attributes["BaseElement.KVLevel"].Value.Replace(' ', '_').ToUpper() + "\"");
                OutStream.WriteAttribute("set", "ERCOTONELINES");
                OutStream.WriteAttribute("origin", SubstationPoint.X, SubstationPoint.Y + 231);
                OutStream.WriteAttribute("xlocked");
                OutStream.WriteAttribute("ylocked");
                OutStream.WriteAttribute("point", SubstationPoint.X, SubstationPoint.Y + 231);
                OutStream.WriteAttribute("point", kvp.Value.X - SubstationPoint.X, kvp.Value.Y - SubstationPoint.Y);
                OutStream.WriteEndTag();
            }

            //Now, write out our control panel
            StringBuilder cPanelParameters = new StringBuilder();
            cPanelParameters.AppendLine("Lines=" + CheckType(OneLine, "Line"));
            cPanelParameters.AppendLine("Transformers=" + CheckType(OneLine, "Transformer"));
            cPanelParameters.AppendLine("Units=" + CheckType(OneLine, "Unit"));
            cPanelParameters.AppendLine("Loads=" + CheckType(OneLine, "Load", "LAAR"));
            cPanelParameters.AppendLine("Shunts=" + CheckType(OneLine, "Capacitor", "Reactor"));
            cPanelParameters.AppendLine("Breakers/Switches=" + CheckType(OneLine, "Breaker", "Switch"));
            cPanelParameters.Append("Nodes=" + CheckType(OneLine, "Node"));

            OutStream.WriteStartTag("symbol", "BTN_POKE_6CHAR_NARROW");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("keyset \"MENU\"");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("menu \"GOTO_ONELINES\"");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("origin", 4, 424);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteEndTag();

            OutStream.WriteTextWithGab("Go To", "TEXT_TITLE_COLUMN_HEADERS", new Point(34, 427), true, true);
            OutStream.WriteStartTag("symbol", "MENU_PULLDOWN_ICON");
            OutStream.WriteAttribute("set", "ERCOTONELINES");
            OutStream.WriteAttribute("origin", 12, 427);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteEndTag();

            //WriteControl(OutStream, new Rectangle(4, 404, 185, 329), "ERCOTControls.ctlControlPanel", "", cPanelParameters);    
        }

        /// <summary>
        /// Write out our control panel
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="BoundingRectangle"></param>
        /// <param name="ControlType"></param>
        /// <param name="Parameters"></param>
        /// <param name="Query"></param>
        private static void WriteControl(MM_DDL_Exporter_Stream OutStream, Rectangle BoundingRectangle, string ControlType, string Query, StringBuilder Parameters)
        {
            OutStream.WriteStartTag("control", "");
            OutStream.WriteAttribute("origin", BoundingRectangle.Left, BoundingRectangle.Top);
            OutStream.WriteAttribute("width", BoundingRectangle.Width);
            OutStream.WriteAttribute("height", BoundingRectangle.Height);
            OutStream.WriteAttribute("xlocked");
            OutStream.WriteAttribute("ylocked");
            OutStream.WriteAttribute("progid", ControlType);

            /*PropertyBag pBag = new PropertyBag();
            pBag.WriteProperty("Query", Query.ToString(), "");
            pBag.WriteProperty("Parameters", Parameters.ToString(), "");
            

            byte[] OutBytes = (byte[])pBag.Contents;
            List<String> OutAttributes = new List<string>();
            int CurCol = 0;
            StringBuilder OutSb = new StringBuilder(OutBytes.Length.ToString() + " ");
            foreach (byte OutByte in OutBytes)
            {
                OutSb.Append(OutByte.ToString("X") + " ");
                CurCol += OutByte.ToString("X").Length;
                if (CurCol > 238)
                {
                    CurCol = 0;
                    OutSb.AppendLine();
                }
            }
            
            //Now, write each row out
            String InLine;
            using (StringReader sRd = new StringReader(OutSb.ToString()))
                while (!String.IsNullOrEmpty(InLine = sRd.ReadLine()))
                    OutStream.WriteAttribute("ctrldata", InLine);
             */
            OutStream.WriteEndTag();
        }

        /// <summary>
        /// Determine whether one or more types are present in our document
        /// </summary>
        /// <param name="OneLine">The one-line to search</param>
        /// <param name="ElemTypes">The element types to look for</param>
        /// <returns></returns>
        private static bool CheckType(MM_Database_OneLine OneLine, params string[] ElemTypes)
        {
            foreach (string str in ElemTypes)
                if (OneLine.Document.SelectNodes("//" + str).Count > 0)
                    return true;
            return false;
        }

        /// <summary>
        /// Write the old style label
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="xDescriptor"></param>
        /// <param name="BaseElement"></param>
        private static void WriteOldLabel(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, XmlElement xDescriptor, CIM_Element BaseElement)
        {
            Point Origin = GetRectangle(xDescriptor.Attributes["Bounds"].Value, null).Location;
            Origin.X += Offset.X;
            Origin.Y += Offset.Y;

            //Locate our element
            CIM_Element LabelElem = MM_Database_CIM.TEIDs[Convert.ToUInt64(xDescriptor.ParentNode.Attributes["BaseElement.TEID"].Value)];

            //If we have an arrow, draw it
            if (XmlConvert.ToBoolean(xDescriptor.ParentNode.Attributes["DescriptorArrow"].Value) || XmlConvert.ToBoolean(xDescriptor.Attributes["DescriptorArrow"].Value))
            {
                Point Pt1, Pt2;
                GetStraightLine(GetRectangle(xDescriptor.Attributes["Bounds"].Value, null), GetRectangle(xDescriptor.ParentNode.Attributes["Bounds"].Value, null), out Pt1, out Pt2);
                Pt1.X += Offset.X; Pt1.Y += Offset.Y;
                Pt2.X += Offset.X; Pt2.Y += Offset.Y;
                OutStream.WriteGraphicLine(Pt1, Pt2, true, "ARROW_FILLED");
            }

            if (xDescriptor.Name == "SecondaryDescriptor" && xDescriptor.ParentNode.Name == "Node")
                OutStream.WriteText(LabelElem.Parse("etx:ElectricalBus<etx:ResourceNode", null).Name, Origin, "LightGray", "Black", "Arial", 10, false, false, true, false, false);

            else if (xDescriptor.ParentNode.Name == "PricingVector")
            {
                CIM_Element EPSMeter = LabelElem.Parse("etx:EPSMeter", null);

                OutStream.WriteText(EPSMeter.Name + "\n" + EPSMeter["etx:EPSMeter.RID"], Origin, "LightGray", "Black", "Arial", 10, false, false, false, false, false);
            }
            else if (xDescriptor.ParentNode.Name == "Line")
            {
                foreach (String str in xDescriptor.ParentNode.Attributes["BaseElement.ConnectedStations"].Value.Split(','))
                    if (str != BaseElement.TEID)
                    {
                        CIM_Element OtherSub = MM_Database_CIM.TEIDs[Convert.ToUInt64(str)];
                        Point LabelPoint = new Point(Origin.X + 3, Origin.Y + 2);
                        Point TelemPoint = new Point(Origin.X + 2, Origin.Y + 15);
                        String LineName = LabelElem.Parse("cim:Line", null).Name + (LabelElem.Name == "1" ? "" : LabelElem.Name);
                        //Find the node on our side
                        foreach (CIM_Element Node in LabelElem.Nodes)
                            if (Node.Substation.Equals(BaseElement))
                                if (SCADA)
                                {
                                    Dictionary<String, CIM_Element> Analogs = LoadAnalogs(LabelElem, BaseElement);
                                    OutStream.WriteDataBoundPicture("DEVICE_SUBSTN", Origin, "SUBSTN=" + OtherSub.Name.ToUpper());
                                    foreach (string AnalogToFind in "MVA,MW,MVAR".Split(','))
                                        if (Analogs.ContainsKey(AnalogToFind))
                                        {
                                            OutStream.WriteDataBoundPicture("ANALOG_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), TelemPoint, "SUBSTN=" + BaseElement.Name.ToUpper(), "DEVTYP=LN", "DEVICE=" + LineName, "ANALOG=" + AnalogToFind);
                                            TelemPoint.Y += 15;
                                        }
                                }
                                else
                                {
                                    OutStream.WriteDataBoundPicture("ANALOG_SUBSTN_NET", Origin, "ST=" + OtherSub.Name.ToUpper());
                                    OutStream.WriteDataBoundPicture("ANALOG_N_LN_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), TelemPoint, "ST=" + BaseElement.Name, "ND=" + Node.Name.ToUpper(), "LN2=" + LineName);
                                }

                        
                        //Write out the name of our line
                        if (!String.IsNullOrEmpty(OtherSub["cim:IdentifiedObject.longName"]))
                            OutStream.WriteTextWithGab(MM_Repository.TitleCase(OtherSub["cim:IdentifiedObject.longName"]), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", LabelPoint, false, false);
                        else
                            OutStream.WriteTextWithGab(MM_Repository.TitleCase(OtherSub.Name), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", LabelPoint, false, false);
                    }
            }
            else if (xDescriptor.ParentNode.Name == "Breaker")
                if (SCADA)
                    OutStream.WriteDataBoundPicture("CB_SW_SCADA_LABEL", Origin, "SUBSTN=" + LabelElem.Substation.Name.ToUpper(), "DEVTYP=CB", "DEVICE=" + LabelElem.Name.ToUpper(), "POINT=ST");
                else
                    OutStream.WriteDataBoundPicture("CB_SW_NET_LABEL", Origin, "ST=" + LabelElem.Substation.Name.ToUpper(), "CBTYP=CB", "CB=" + LabelElem.Name.ToUpper());
            else if (xDescriptor.ParentNode.Name == "Switch")
                if (SCADA)
                    OutStream.WriteDataBoundPicture("CB_SW_SCADA_LABEL", Origin, "SUBSTN=" + LabelElem.Substation.Name.ToUpper(), "DEVTYP=DSC", "DEVICE=" + LabelElem.Name.ToUpper(), "POINT=ST");
                else
                    OutStream.WriteDataBoundPicture("CB_SW_NET_LABEL", Origin, "ST=" + LabelElem.Substation.Name.ToUpper(), "CBTYP=DSC", "CB=" + LabelElem.Name.ToUpper());
            else if (xDescriptor.ParentNode.Name == "Unit" && SCADA)
            {
                Dictionary<String, CIM_Element> Analogs = LoadAnalogs(LabelElem, BaseElement);
                OutStream.WriteDataBoundPicture("GEN_LABEL", Origin, "PL=" + LabelElem.Substation.Name, "PLC=" + LabelElem.Name, "UNIT=" + LabelElem.Name);
                foreach (String AnalogToFind in "MW,MVAR,MVA".Split(','))
                    if (Analogs.ContainsKey(AnalogToFind))
                    {
                        OutStream.WriteDataBoundPicture("ANALOG_10PNT_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), Origin, "SUBSTN=" + LabelElem.Name.ToUpper(), "DEVTYP=UN", "DEVICE=" + LabelElem.Name, "ANALOG=" + AnalogToFind);
                        Origin.Y += 15;
                    }
                Origin.Y += 15;
                Origin.X += 15;
                if (Analogs.ContainsKey("AVR"))
                    OutStream.WriteDataBoundPicture("AVR", Origin, "SUBSTN=" + LabelElem.Name.ToUpper(), "DEVTYP=UN", "DEVICE=" + LabelElem.Name, "ANALOG=AVR");
            }
            else if (xDescriptor.ParentNode.Name == "Unit")
            {
                OutStream.WriteDataBoundPicture("GEN_LABEL_NET", Origin, "ST=" + BaseElement.Name.ToUpper(), "UN=" + LabelElem.Name.ToUpper());
                OutStream.WriteDataBoundPicture("ANALOG_N_UN_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), new Point(Origin.X + 2, Origin.Y + 15), "ST=" + BaseElement.Name.ToUpper(), "UN=" + LabelElem.Name.ToUpper());
            }
            else if ((xDescriptor.ParentNode.Name == "Load" || xDescriptor.ParentNode.Name.Equals("LAAR", StringComparison.CurrentCultureIgnoreCase)) && SCADA)
            {
                //Start by writing our label
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                Dictionary<String, CIM_Element> Analogs = LoadAnalogs(LabelElem, LabelElem.Substation);
                Origin.X -= 30;
                foreach (string AnalogToFind in "MVA,MW,MVAR".Split(','))
                    if (Analogs.ContainsKey(AnalogToFind))
                    {
                        Origin.Y += 15;
                        OutStream.WriteDataBoundPicture("ANALOG_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), Origin, "SUBSTN=" + LabelElem.Substation.Name, "DEVTYP=LD", "DEVICE=" + LabelElem.Name, "ANALOG=" + AnalogToFind);
                    }
            }
            else if (xDescriptor.ParentNode.Name == "Load" || xDescriptor.ParentNode.Name.Equals("LAAR", StringComparison.CurrentCultureIgnoreCase))
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                OutStream.WriteDataBoundPicture("ANALOG_N_LD_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), new Point(Origin.X + 2, Origin.Y + 15), "ST=" + BaseElement.Name.ToUpper(), "LD=" + LabelElem.Name.ToUpper());
            }
            else if (xDescriptor.ParentNode.Name == "StaticVarCompensator" && SCADA)
            {
                //Start by writing our label
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                Dictionary<String, CIM_Element> Analogs = LoadAnalogs(LabelElem, LabelElem.Substation);
                Origin.X -= 30;
                foreach (string AnalogToFind in "MVA,MW,MVAR".Split(','))
                    if (Analogs.ContainsKey(AnalogToFind))
                    {
                        Origin.Y += 15;
                        OutStream.WriteDataBoundPicture("ANALOG_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), Origin, "SUBSTN=" + LabelElem.Substation.Name, "DEVTYP=SVS", "DEVICE=" + LabelElem.Name, "ANALOG=" + AnalogToFind);
                    }
            }
            else if (xDescriptor.ParentNode.Name == "StaticVarCompensator")
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                OutStream.WriteDataBoundPicture("ANALOG_N_SVS_" + xDescriptor.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), new Point(Origin.X + 2, Origin.Y + 15), "ST=" + BaseElement.Name.ToUpper(), "LD=" + LabelElem.Name.ToUpper());
            }
            else if ((xDescriptor.ParentNode.Name == "Node" || xDescriptor.ParentNode.Name == "PokePoint") && SCADA)
            {
                //First, write our label
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                CIM_Element Bus = LabelElem.Parse("cim:BusbarSection", null);
                if (Bus != null)
                {
                    Dictionary<String, CIM_Element> Analogs = LoadAnalogs(Bus, LabelElem.Substation);
                    if (Analogs.ContainsKey("KV"))
                        OutStream.WriteDataBoundPicture("KV_LABEL", Origin, "SUBSTN=" + LabelElem.Substation.Name, "DEVTYP=BS", "DEVICE=" + LabelElem.Name, "ANALOG=" + LabelElem.Name);
                }

            }
            else if (xDescriptor.ParentNode.Name == "Node" || xDescriptor.ParentNode.Name == "PokePoint")
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
                //OutStream.WriteDataBoundPicture("KV_NET_LABEL", Origin, "ST=" + BaseElement.Name.ToUpper(), "ND=" + LabelElem.Name.ToUpper());
                OutStream.WriteDataBoundPicture("KV_NETWORK", new Point(Origin.X, Origin.Y + 10), "ST=" + BaseElement.Name.ToUpper(), "ND=" + LabelElem.Name.ToUpper());
            }
            else if (xDescriptor.ParentNode.Name == "Transformer" && SCADA)
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);

                //Match up our analogs to their associated windings
                Dictionary<CIM_Element, Dictionary<String, CIM_Element>> WindingAnalogs = new Dictionary<CIM_Element, Dictionary<string, CIM_Element>>();
                foreach (CIM_Element AssociatedWinding in LabelElem.Links)
                    if (AssociatedWinding.Type == "cim:TransformerWinding")                
                {
                    Dictionary<String, CIM_Element> OutArray = new Dictionary<string, CIM_Element>();
                    foreach (CIM_Element Analog in LabelElem.Links)
                        if (Analog.Type == "cim:Analog")
                            if (!XmlConvert.ToBoolean(Analog["etx:Analog.forNetwork"]) && Analog.Parse("cim:Terminal>cim:TransformerWinding", null).Equals(AssociatedWinding))
                                OutArray.Add(Analog.Name, Analog);
                    WindingAnalogs.Add(AssociatedWinding, OutArray);
                }

                /* foreach (KeyValuePair<String, CIM_Element> kvp in LoadAnalogs(LabelElem, LabelElem.Substation))
                 {
                     CIM_Element AssociatedWinding = kvp.Value.Parse("cim:Terminal>cim:TransformerWinding", null);
                     Dictionary<String, CIM_Element> OutArray;
                     if (!WindingAnalogs.TryGetValue(AssociatedWinding, out OutArray))
                         WindingAnalogs.Add(AssociatedWinding, OutArray = new Dictionary<string, CIM_Element>());
                     OutArray.Add(kvp.Key, kvp.Value);
                 }*/

                //Now, go through our windings, write out our according labels
                bool IsVertical = xDescriptor.ParentNode.Attributes["Orientation"].Value == "Vertical" || xDescriptor.ParentNode.Attributes["Orientation"].Value == "Up" || xDescriptor.ParentNode.Attributes["Orientation"].Value == "Down";
                bool FirstNode = true;
                foreach (XmlElement xWinding in xDescriptor.ParentNode.SelectNodes("Winding"))
                {
                    int XOffset = IsVertical ? 0 : (FirstNode ? -27 : 15);
                    int YOffset = IsVertical ? (FirstNode ? -27 : 15) : 15;
                    FirstNode = false;
                    CIM_Element FoundAnalog;
                    if ((!xWinding.HasAttribute("Visible") || XmlConvert.ToBoolean(xWinding.Attributes["Visible"].Value)) && xWinding.HasAttribute("BaseElement.KVLevel"))
                    {
                        CIM_Element Winding = MM_Database_CIM.TEIDs[XmlConvert.ToUInt64(xWinding.Attributes["BaseElement.TEID"].Value)];
                        if (WindingAnalogs.ContainsKey(Winding))
                            foreach (String AnalogToFind in (FirstNode ? "TAP,MVAR,MW,MVA" : "MVA,MW,MVAR,TAP").Split(','))
                                if (WindingAnalogs[Winding].TryGetValue(AnalogToFind, out FoundAnalog))
                                {
                                    String XFType = "XF";
                                    CIM_Element Type = Winding.Parse("etx:MeasurementLocation>etx:DeviceType", null);
                                    if (Type != null)
                                        XFType = Type.Name;
                                    OutStream.WriteDataBoundPicture("ANALOG_10PNT_" + (AnalogToFind == "TAP" ? "TAP" : xWinding.ParentNode.Attributes["Orientation"].Value.Substring(0, 1)), new Point(Origin.X + XOffset, Origin.Y + YOffset), "SUBSTN=" + LabelElem.Substation.Name, "DEVTYP=" + XFType, "DEVICE=" + Winding.Name, "ANALOG=" + AnalogToFind);
                                    if (IsVertical)
                                        YOffset += FirstNode ? -15 : 15;
                                    else
                                        XOffset += FirstNode ? -15 : 15;
                                }
                    }
                }

            }
            else if (xDescriptor.ParentNode.Name == "Transformer")
            {
                OutStream.WriteDataBoundPicture("TRANS_LABEL_NET", Origin, "ST=" + BaseElement.Name.ToUpper(), "XF=" + LabelElem.Name.ToUpper());
                bool Vertical = xDescriptor.ParentNode.Attributes["Orientation"].Value == "Vertical" || xDescriptor.ParentNode.Attributes["Orientation"].Value == "Up" || xDescriptor.ParentNode.Attributes["Orientation"].Value == "Down";
                bool FirstNode = true;
                foreach (XmlElement xWinding in xDescriptor.ParentNode.SelectNodes("Winding"))
                {
                    int XOffset = Vertical ? 0 : FirstNode ? -27 : 15;
                    int YOffset = Vertical ? (FirstNode ? -27 : 15) : 0;
                    FirstNode = false;
                    if ((!xWinding.HasAttribute("Visible") || XmlConvert.ToBoolean(xWinding.Attributes["Visible"].Value)) && xWinding.HasAttribute("BaseElement.TEID"))
                        foreach (CIM_Element Node in MM_Database_CIM.TEIDs[XmlConvert.ToUInt64(xWinding.Attributes["BaseElement.TEID"].Value)].Nodes)
                            if (Node.Type == "cim:ConnectivityNode")
                                OutStream.WriteDataBoundPicture("ANALOG_N_XF_" + xWinding.ParentNode.Attributes["Orientation"].Value.Substring(0, 1), new Point(Origin.X + XOffset, Origin.Y + YOffset), "ST=" + LabelElem.Substation.Name, "ND=" + Node.Name, "XF2=" + LabelElem.Name);
                }
            }
            else if (xDescriptor.ParentNode.Name == "Capacitor")
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
            }
            else if (xDescriptor.ParentNode.Name == "Reactor")
            {
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
            }
            else if (xDescriptor.ParentNode.Name == "EndCap")
                OutStream.WriteTextWithGab(LabelElem.Name.ToUpper(), "TEXT_FV_CHR_NONENTRY_SIMPLE_ERCOT", Origin, false, false);
            else
                throw new InvalidOperationException("Unknown label type: " + LabelElem.Name);


        }

        /// <summary>
        /// Locate all analogs associated with SCADA that are connected to the specified substation
        /// </summary>
        /// <param name="BaseElement">The element to parse</param>
        /// <param name="SubstationElement">The required substatoin</param>
        /// <returns></returns>
        private static Dictionary<String, CIM_Element> LoadAnalogs(CIM_Element BaseElement, CIM_Element SubstationElement)
        {
            Dictionary<String, CIM_Element> Analogs = new Dictionary<string, CIM_Element>();
            foreach (CIM_Element Analog in BaseElement.Links)
                if (Analog.Type == "cim:Analog")            
                if (!XmlConvert.ToBoolean(Analog["etx:Analog.forNetwork"]) && (Analog.Parse("etx:PseudoTerminal") != null || Analog.Parse("cim:Terminal").Substation.Equals(SubstationElement)))
                    if (!Analogs.ContainsKey(Analog.Name))
                        Analogs.Add(Analog.Name, Analog);
            return Analogs;
        }

        /// <summary>
        /// Write out the label for an element
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="xDescriptor"></param>
        /// <param name="BaseElement"></param>
        private static void WriteLabel(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, XmlElement xDescriptor, CIM_Element BaseElement)
        {
            Rectangle Bounds = GetRectangle(xDescriptor.Attributes["Bounds"].Value, null);
            Bounds.X += Offset.X;
            Bounds.Y += Offset.Y;
            if (xDescriptor.Name == "SecondaryDescriptor")
                OutStream.WriteText(xDescriptor.Attributes["Value"].Value, Bounds.Location, "White", "Black", "Arial", 10, false, false, true, false, false);
            else
            {


                //If we have a breaker or switch, write that label
                Dictionary<String, String> OutParameters = new Dictionary<string, string>();
                String ControlType, Template;
                OutParameters.Add("Name", xDescriptor.ParentNode.Attributes["BaseElement.Name"].Value.ToUpper());
                OutParameters.Add("guid", Guid.NewGuid().ToString());
                OutParameters.Add("ElemType", xDescriptor.ParentNode.Name);
                if (xDescriptor.ParentNode.Attributes["BaseElement.KVLevel"] != null)
                    OutParameters.Add("Voltage", xDescriptor.ParentNode.Attributes["BaseElement.KVLevel"].Value.Split(' ')[0]);
                OutParameters.Add("TEID", BaseElement.TEID);
                OutParameters.Add("SubName", OneLine.Document.DocumentElement.Attributes["BaseElement.Name"].Value.ToUpper());
                OutParameters.Add("SubLongName", OneLine.Document.DocumentElement.Attributes["BaseElement.LongName"].Value);
                OutParameters.Add("Comments", "");
                OutParameters.Add("HasSCADA", "true");
                if (BaseElement.Owner.Equals(BaseElement.Operator))
                    OutParameters.Add("OwnerOperator", MM_Repository.TitleCase(BaseElement.Owner.Name) + "\\Phone:" + BaseElement.Owner["etx:Company.PrimaryPhone"] + "\\Phone:" + BaseElement.Owner["etx:Company.DUNS"]);
                else
                {
                    OutParameters.Add("Owner", MM_Repository.TitleCase(BaseElement.Owner.Name) + "\\Phone:" + BaseElement.Owner["etx:Company.PrimaryPhone"] + "\\Phone:" + BaseElement.Owner["etx:Company.DUNS"]);
                    OutParameters.Add("Operator", MM_Repository.TitleCase(BaseElement.Operator.Name) + "\\Phone:" + BaseElement.Operator["etx:Company.PrimaryPhone"] + "\\Phone:" + BaseElement.Operator["etx:Company.DUNS"]);
                }

                //Now, handle element-specific data            
                if (xDescriptor.ParentNode.Name == "Breaker" || xDescriptor.ParentNode.Name == "Switch")
                {
                    if (xDescriptor.HasAttribute("BaseElement.HasSynchroscope"))
                        OutParameters.Add("HasSynchroscope", xDescriptor.ParentNode.Attributes["BaseElement.HasSynchroscope"].Value);
                    else
                        OutParameters.Add("HasSynchroscope", "false");
                    OutParameters.Add("BreakerType", xDescriptor.ParentNode.Name == "Breaker" ? "CB" : "DSC");
                    Template = "Breaker Switch Template.xml";
                    ControlType = "ctlBreakerSwitch";
                }
                else if (xDescriptor.ParentNode.Name == "Line")
                {
                    foreach (String str in xDescriptor.ParentNode.Attributes["BaseElement.ConnectedStations"].Value.Split(','))
                        if (str != BaseElement.TEID)
                        {
                            CIM_Element OtherSub = MM_Database_CIM.TEIDs[Convert.ToUInt64(str)];
                            OutParameters.Add("OtherSubName", OtherSub.Name.ToUpper());
                            OutParameters.Add("OtherSub", OtherSub.Name.ToUpper());
                            OutParameters.Add("OtherSubLongName", OtherSub["cim:IdentifiedObject.description"]);

                        }

                    OutParameters.Add("Distance", xDescriptor.ParentNode.Attributes["BaseElement.Length"].Value);
                    OutParameters.Add("Angle", xDescriptor.ParentNode.Attributes["BaseElement.Angle"].Value);
                    OutParameters.Add("County", "Unknown");

                    Template = "Line Template.xml";
                    ControlType = "ctlLine";
                }
                else if (xDescriptor.ParentNode.Name == "Load")
                {
                    Template = "Load Template.xml";
                    ControlType = "ctlLine";
                }
                else if (xDescriptor.ParentNode.Name == "Capacitor" || xDescriptor.ParentNode.Name == "Reactor")
                {
                    Template = "Capacitor Reactor Template.xml";
                    ControlType = "ctlCapacitorReactor";
                }
                else if (xDescriptor.ParentNode.Name == "Node")
                {
                    Template = "Node Template.xml";
                    ControlType = "ctlNode";
                }
                else if (xDescriptor.ParentNode.Name == "Unit")
                {
                    Template = "Unit Template.xml";
                    ControlType = "ctlUnit";
                }
                else if (xDescriptor.ParentNode.Name == "Transformer")
                {
                    Template = "Transformer Template.xml";
                    ControlType = "ctlTransformer";
                }
                else
                    throw new InvalidOperationException("Unknown target label type!");

                //Now, write out our control
                StringBuilder OutParams = new StringBuilder();
                String OutQuery;
                using (StreamReader sRd = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("MacomberMapSystem.Common.Exports." + Template)))
                    OutQuery = sRd.ReadToEnd();
                foreach (KeyValuePair<String, String> kvp in OutParameters)
                {
                    OutParams.AppendLine(kvp.Key + "=" + kvp.Value);
                    OutQuery = OutQuery.Replace("{" + kvp.Key + "}", kvp.Value.ToUpper());
                }

                WriteControl(OutStream, Bounds, "ERCOTControls." + ControlType, OutQuery, OutParams);
            }

        }

        /// <summary>
        /// Write out the image for an element
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="BaseElement"></param>
        /// <param name="xElem"></param>
        private static void WriteElement(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, CIM_Element BaseElement, XmlElement xElem)
        {
            String KVLevel = "?";
            if (xElem.HasAttribute("BaseElement.KVLevel"))
                KVLevel = xElem.Attributes["BaseElement.KVLevel"].Value.Replace(" ", "").ToUpper();
            Rectangle Bounds = GetRectangle(xElem.Attributes["Bounds"].Value, null);
            CIM_Element Elem = MM_Database_CIM.Elements[new MM_rdfID(xElem.Attributes["rdfID"].Value)];

            if (xElem.Name == "Breaker" || xElem.Name == "Switch")
                if (SCADA)
                    OutStream.WriteDataBoundPicture((xElem.Name == "Breaker" ? "CB_" : "SW_") + (SCADA ? "" : "NET_") + KVLevel + "_" + xElem.Attributes["Orientation"].Value.Substring(0, 1), new Point(Offset.X + Bounds.Left + 1, Offset.Y + Bounds.Top + 1), "SUBSTN=" + Elem.Substation.Name, (xElem.Name == "Breaker" ? "DEVTYP=CB" : "DEVTYP=DSC"), "DEVICE=" + Elem.Name, "POINT=ST");
                else
                    OutStream.WriteDataBoundPicture((xElem.Name == "Breaker" ? "CB_" : "SW_") + (SCADA ? "" : "NET_") + KVLevel + "_" + xElem.Attributes["Orientation"].Value.Substring(0, 1), new Point(Offset.X + Bounds.Left + 1, Offset.Y + Bounds.Top + 1), "ST=" + Elem.Substation.Name, (xElem.Name == "Breaker" ? "CBTYP=CB" : "CBTYP=DSC"), "CB=" + Elem.Name);
            else if (xElem.Name == "Load" || xElem.Name.Equals("LAAR", StringComparison.CurrentCultureIgnoreCase) || xElem.Name == "Line" || xElem.Name == "Reactor" || xElem.Name == "Capacitor")
                OutStream.WriteSymbol(xElem.Name.ToUpper() + "_" + xElem.Attributes["BaseElement.KVLevel"].Value.Replace(" ", "").ToUpper() + "_" + xElem.Attributes["Orientation"].Value.Substring(0, 1), new Point(Bounds.Left + 1 + Offset.X, Offset.Y + Bounds.Top + 1));
            else if (xElem.Name == "StaticVarCompensator")
                OutStream.WriteSymbol("SVC_" + xElem.Attributes["BaseElement.KVLevel"].Value.Replace(" ", "").ToUpper() + "_" + xElem.Attributes["Orientation"].Value.Substring(0, 1), new Point(Bounds.Left + 1 + Offset.X, Offset.Y + Bounds.Top + 1));
            else if (xElem.Name == "EndCap")
                OutStream.WriteSymbol(xElem.Name.ToUpper() + "_" + xElem.Attributes["BaseElement.KVLevel"].Value.Replace(" ", "").ToUpper(), new Point(Bounds.Left + 1 + Offset.X, Offset.Y + Bounds.Top + 1));
            else if (xElem.Name == "Unit")
                OutStream.WriteDataBoundPicture("GEN_NET_" + KVLevel + "_" + xElem.Attributes["Orientation"].Value.Substring(0, 1), new Point(Bounds.Left + 1 + Offset.X, Bounds.Top + Offset.Y + 1), "ST=" + Elem.Substation.Name, "UN=" + Elem.Name);
            else if (xElem.Name == "Transformer")
            {
                foreach (XmlElement xWinding in xElem.SelectNodes("Winding"))
                    if ((!xWinding.HasAttribute("Visible") || XmlConvert.ToBoolean(xWinding.Attributes["Visible"].Value)) && xWinding.HasAttribute("BaseElement.KVLevel"))
                    {
                        Rectangle Bounds2 = GetRectangle(xWinding.Attributes["Bounds"].Value, null);
                        OutStream.WriteSymbol("TRANS_" + xWinding.Attributes["BaseElement.KVLevel"].Value.ToUpper().Replace(" ", "") + "_" + xWinding.Attributes["Orientation"].Value.Substring(0, 1), new Point(Bounds.Left + Offset.X + Bounds2.X, Offset.Y + Bounds.Top + 1 + Bounds2.Y));
                    }
            }
            else
                throw new InvalidOperationException("Unknown element: " + xElem.Name);


            //Write out our label
            bool OldLabel = true;
            if (xElem["Descriptor"] != null)
                if (OldLabel)
                    WriteOldLabel(OutStream, OneLine,  SCADA, xElem["Descriptor"], BaseElement);
                else
                    WriteLabel(OutStream, OneLine,  SCADA, xElem["Descriptor"], BaseElement);
            if (xElem["SecondaryDescriptor"] != null)
                if (OldLabel)
                    WriteOldLabel(OutStream, OneLine,  SCADA, xElem["SecondaryDescriptor"], BaseElement);
                else
                    WriteLabel(OutStream, OneLine,  SCADA, xElem["SecondaryDescriptor"], BaseElement);

        }

        /// <summary>
        /// Create a rectangle from a string
        /// </summary>
        /// <param name="RectString"></param>
        /// <param name="p_2"></param>
        /// <returns></returns>
        private static Rectangle GetRectangle(string RectString, object p_2)
        {
            string[] splStr = RectString.Split(',');
            return new Rectangle(int.Parse(splStr[0]), int.Parse(splStr[1]), int.Parse(splStr[2]), int.Parse(splStr[3]));
        }

        /// <summary>
        /// Write out the image and connectivity of a node
        /// </summary>
        /// <param name="OutStream"></param>
        /// <param name="OneLine"></param>
        /// <param name="SCADA"></param>
        /// <param name="BaseElement"></param>
        /// <param name="xNode"></param>
        private static void WriteNode(MM_DDL_Exporter_Stream OutStream, MM_Database_OneLine OneLine, bool SCADA, CIM_Element BaseElement, XmlElement xNode)
        {
            bool OldLabel = true;
            CIM_Element Node = MM_Database_CIM.TEIDs[XmlConvert.ToUInt64(xNode.Attributes["BaseElement.TEID"].Value)];
            String KVLevel = xNode.Attributes["BaseElement.KVLevel"].Value.Replace(' ', '_').ToUpper();
            //MM_KVLevel KVLevel = Data_Manager.KVLevels[xNode.Attributes["BaseElement.KVLevel"].Value];
            Rectangle NodeBounds = GetRectangle(xNode.Attributes["Bounds"].Value,null);
                

            //Go through each target element hand handle accordingly
            Point Pt1, Pt2;
            foreach (XmlElement xCh in xNode.ChildNodes)
                if (xCh.Name != "Descriptor" && xCh.Name != "SecondaryDescriptor")
                {
                    Rectangle LastRectangle = NodeBounds;

                    foreach (XmlElement xCh2 in xCh.ChildNodes)
                    {

                        //First, draw our line from here to there
                        Rectangle ThisRectangle = GetRectangle(xCh2.Attributes["Bounds"].Value, null);
                        GetStraightLine(LastRectangle, ThisRectangle, out Pt1, out Pt2);
                        Pt1.X += Offset.X;
                        Pt1.Y += Offset.Y;
                        Pt2.X += Offset.X;
                        Pt2.Y += Offset.Y;

                        OutStream.WriteGraphicLine(Pt1, Pt2, false, KVLevel);
                        LastRectangle = ThisRectangle;

                        //Now, if we have a visible poke point, jumper, or pricing vector, handle it.
                        if (xCh2.Name == "PokePoint")
                        {
                            // if (XmlConvert.ToBoolean(xCh2.Attributes["IsVisible"].Value))
                            if (XmlConvert.ToBoolean(xCh2.Attributes["IsJumper"].Value))
                                OutStream.WriteSymbol("JUMPER_" + KVLevel.Replace("_", "") + "_" + xCh2.Attributes["Orientation"].Value.Substring(0, 1), GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, new Point(Offset.X - 1, Offset.Y));
                            else
                                OutStream.WriteRectangle(GetRectangle(xCh2.Attributes["Bounds"].Value, null), Offset, KVLevel);
                        }
                        else if (xCh2.Name == "PricingVector")
                            if (XmlConvert.ToBoolean(xCh2.Attributes["IsPositive"].Value))
                                OutStream.WriteSymbol("PRICING_VECTOR_" + KVLevel + "_" + xCh2.Attributes["Orientation"].Value.Substring(0, 1), GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, Offset);
                            else if (xCh2.Attributes["Orientation"].Value[0] == 'U')
                                OutStream.WriteSymbol("PRICING_VECTOR_" + KVLevel + "_D", GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, Offset);
                            else if (xCh2.Attributes["Orientation"].Value[0] == 'D')
                                OutStream.WriteSymbol("PRICING_VECTOR_" + KVLevel + "_U", GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, Offset);
                            else if (xCh2.Attributes["Orientation"].Value[0] == 'L')
                                OutStream.WriteSymbol("PRICING_VECTOR_" + KVLevel + "_R", GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, Offset);
                            else if (xCh2.Attributes["Orientation"].Value[0] == 'R')
                                OutStream.WriteSymbol("PRICING_VECTOR_" + KVLevel + "_L", GetRectangle(xCh2.Attributes["Bounds"].Value, null).Location, Offset);
                            else
                                throw new InvalidOperationException("Unknown vector orientation");
                        else
                            throw new InvalidOperationException("Unknown item error");

                        //Check for labels
                        if (xCh2["Descriptor"] != null)
                            if (OldLabel)
                                WriteOldLabel(OutStream, OneLine,  SCADA, xCh2["Descriptor"], BaseElement);
                            else
                                WriteLabel(OutStream, OneLine,  SCADA, xCh2["Descriptor"], BaseElement);
                        if (xCh2["SecondaryDescriptor"] != null)
                            if (OldLabel)
                                WriteOldLabel(OutStream, OneLine,  SCADA, xCh2["SecondaryDescriptor"], BaseElement);
                            else
                                WriteLabel(OutStream, OneLine,  SCADA, xCh2["SecondaryDescriptor"], BaseElement);

                    }

                    //Now, draw a line to our node
                    Rectangle TargetElement = GetRectangle(xNode.OwnerDocument.DocumentElement.SelectSingleNode("Elements/" + xCh.Name + "[@BaseElement.TEID='" + xCh.Attributes["TEID"].Value + "']").Attributes["Bounds"].Value, null);
                    GetStraightLine(LastRectangle, TargetElement, out Pt1, out Pt2);
                    Pt1.X += Offset.X;
                    Pt1.Y += Offset.Y;
                    Pt2.X += Offset.X;
                    Pt2.Y += Offset.Y;

                    OutStream.WriteGraphicLine(Pt1, Pt2, false, KVLevel);


                }


            //We have a rectangular node, so we have to set up things to work properly.
            OutStream.WriteRectangle(NodeBounds, Offset, KVLevel);

            //Write out our labels


            if (xNode["Descriptor"] != null)
                if (OldLabel)
                    WriteOldLabel(OutStream, OneLine,  SCADA, xNode["Descriptor"], BaseElement);
                else
                    WriteLabel(OutStream, OneLine,  SCADA, xNode["Descriptor"], BaseElement);
            if (xNode["SecondaryDescriptor"] != null)
                if (OldLabel)
                    WriteOldLabel(OutStream, OneLine,  SCADA, xNode["SecondaryDescriptor"], BaseElement);
                else
                    WriteLabel(OutStream, OneLine, SCADA, xNode["SecondaryDescriptor"], BaseElement);

        }

        /// <summary>
        /// Determine the paths between elements
        /// </summary>
        /// <param name="xNode">The XML configuration for the node</param>
        /// <param name="BaseBounds">The bounds of the node</param>
        /// <returns></returns>
        private static List<GraphicsPath> DerivePaths(XmlElement xNode, Rectangle BaseBounds)
        {
            List<GraphicsPath> OutPath = new List<GraphicsPath>();
            foreach (XmlElement xElem in xNode.ChildNodes)
                if (xElem.Name != "Descriptor" && xElem.Name != "SecondaryDescriptor")
                {
                    XmlElement LastElement = xElem;
                    GraphicsPath NewPath = new GraphicsPath();
                    foreach (XmlElement xCh in xElem.ChildNodes)
                        AddStraightLine(NewPath, LastElement, LastElement = xCh);
                    AddStraightLine(NewPath, LastElement, xNode);
                    OutPath.Add(NewPath);
                }
            return OutPath;
        }

        /// <summary>
        /// Retrieve a straight line
        /// </summary>
        /// <param name="FromRect"></param>
        /// <param name="ToRect"></param>
        /// <param name="Center1"></param>
        /// <param name="Center2"></param>
        public static void GetStraightLine(Rectangle FromRect, Rectangle ToRect, out Point Center1, out Point Center2)
        {
            Center1 = MM_OneLine_Element.CenterRect(FromRect);
            Center2 = MM_OneLine_Element.CenterRect(ToRect);


            //Adjust the first point if need be
            if (true)
            //if (FromElement.Name == "Node" || FromElement.Name == "PokePoint")
            {
                if (Center2.X >= FromRect.Left && Center2.X <= FromRect.Right)
                    Center1.X = Center2.X;
                else if (Center2.X > FromRect.Right)
                    Center1.X = FromRect.Right;
                else if (Center2.X < FromRect.Left)
                    Center1.X = FromRect.Left;


                if (Center2.Y >= FromRect.Top && Center2.Y <= FromRect.Bottom)
                    Center1.Y = Center2.Y;
                else if (Center2.Y < FromRect.Top)
                    Center1.Y = FromRect.Top;
                else if (Center2.Y > FromRect.Bottom)
                    Center1.Y = FromRect.Bottom;
            }

            //Adjust the second point if need be
            if (true)
            //if (ToElement.Name == "Node" || ToElement.Name == "PokePoint")
            {
                if (Center1.X >= ToRect.Left && Center1.X <= ToRect.Right)
                    Center2.X = Center1.X;
                else if (Center1.X > ToRect.Right)
                    Center2.X = ToRect.Right;
                else if (Center1.X < ToRect.Left)
                    Center2.X = ToRect.Left;

                if (Center1.Y >= ToRect.Top && Center1.Y <= ToRect.Bottom)
                    Center2.Y = Center1.Y;
                else if (Center1.Y < ToRect.Top)
                    Center2.Y = ToRect.Top;
                else if (Center1.Y > ToRect.Bottom)
                    Center2.Y = ToRect.Bottom;
            }

            //Shift our points out to the edge
            if (Center1.X == Center2.X && Center1.Y < Center2.Y)
            {
                Center1.Y = FromRect.Bottom - 2;
                Center2.Y = ToRect.Top + 2;
            }
            else if (Center1.X == Center2.X && Center1.Y > Center2.Y)
            {
                Center1.Y = ToRect.Bottom - 2;
                Center2.Y = FromRect.Top + 2;
            }

            else if (Center1.Y == Center2.Y && Center1.X < Center2.X)
            {
                Center1.X = FromRect.Right - 2;
                Center2.X = ToRect.Left + 2;
            }
            else if (Center1.Y == Center2.Y && Center1.X > Center2.X)
            {
                Center1.X = ToRect.Right - 2;
                Center2.X = FromRect.Left + 2;
            }
        }

        /// <summary>
        /// Add a straight line between to rectangles
        /// </summary>
        /// <param name="gp"></param>
        /// <param name="FromElement"></param>
        /// <param name="ToElement"></param>
        private static void AddStraightLine(GraphicsPath gp, XmlElement FromElement, XmlElement ToElement)
        {

            //Determine our original centers
            Rectangle FromRect = Rectangle.Empty, ToRect = Rectangle.Empty;


            if (FromElement.Attributes.Count == 1)
            {
                foreach (XmlElement xCh in FromElement.SelectNodes("/One_Line/Elements/" + FromElement.Name))
                    if (xCh.Attributes["BaseElement.TEID"].Value == FromElement.Attributes["TEID"].Value)
                        FromRect = GetRectangle(xCh.Attributes["Bounds"].Value, null);
            }
            else
                FromRect = GetRectangle(FromElement.Attributes["Bounds"].Value, null);
            if (ToElement.Attributes.Count == 1)
            {
                foreach (XmlElement xCh in ToElement.SelectNodes("/One_Line/Elements/" + FromElement.Name))
                    if (xCh.Attributes["BaseElement.TEID"].Value == ToElement.Attributes["TEID"].Value)
                        ToRect = GetRectangle(xCh.Attributes["Bounds"].Value, null);
            }
            else
                ToRect = GetRectangle(ToElement.Attributes["Bounds"].Value, null);
            Point Center1 = MM_OneLine_Element.CenterRect(FromRect);
            Point Center2 = MM_OneLine_Element.CenterRect(ToRect);


            //Adjust the first point if need be

            if (FromElement.Name == "Node" || FromElement.Name == "PokePoint")
            {
                if (Center2.X >= FromRect.Left && Center2.X <= FromRect.Right)
                    Center1.X = Center2.X;
                else if (Center2.X > FromRect.Right)
                    Center1.X = FromRect.Right;
                else if (Center2.X < FromRect.Left)
                    Center1.X = FromRect.Left;


                if (Center2.Y >= FromRect.Top && Center2.Y <= FromRect.Bottom)
                    Center1.Y = Center2.Y;
                else if (Center2.Y < FromRect.Top)
                    Center1.Y = FromRect.Top;
                else if (Center2.Y > FromRect.Bottom)
                    Center1.Y = FromRect.Bottom;
            }

            //Adjust the second point if need be
            if (ToElement.Name == "Node" || ToElement.Name == "PokePoint")
            {
                if (Center1.X >= ToRect.Left && Center1.X <= ToRect.Right)
                    Center2.X = Center1.X;
                else if (Center1.X > ToRect.Right)
                    Center2.X = ToRect.Right;
                else if (Center1.X < ToRect.Left)
                    Center2.X = ToRect.Left;

                if (Center1.Y >= ToRect.Top && Center1.Y <= ToRect.Bottom)
                    Center2.Y = Center1.Y;
                else if (Center1.Y < ToRect.Top)
                    Center2.Y = ToRect.Top;
                else if (Center1.Y > ToRect.Bottom)
                    Center2.Y = ToRect.Bottom;
            }

            //Shift our points out to the edge
            if (Center1.X == Center2.X && Center1.Y < Center2.Y)
            {
                Center1.Y = FromRect.Bottom - 2;
                Center2.Y = ToRect.Top + 2;
            }
            else if (Center1.X == Center2.X && Center1.Y > Center2.Y)
            {
                Center1.Y = ToRect.Bottom - 2;
                Center2.Y = FromRect.Top + 2;
            }

            else if (Center1.Y == Center2.Y && Center1.X < Center2.X)
            {
                Center1.X = FromRect.Right - 2;
                Center2.X = ToRect.Left + 2;
            }
            else if (Center1.Y == Center2.Y && Center1.X > Center2.X)
            {
                Center1.X = ToRect.Right - 2;
                Center2.X = FromRect.Left + 2;
            }


            gp.AddLine(Center1, Center2);
        }


        #endregion
    }
}
